package servlets;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import services.GeneralFunctions;
import services.ImageFunctions;

/**
 * Класс реализации сервлета UpdateImageServlet, который используется для 
 * динамического предпросмотра редактируемого изображения, а также содержит
 * статические методы-обертки функций обработки изображений для вызова из
 * jsp-страницы.
 */
@WebServlet("/UpdateImageServlet")
public class UpdateImageServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

    public UpdateImageServlet() { }

	/**
	 * Метод, к которому идет обращение из javascript-функции с "просьбой"
	 * отредактировать изображение.
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String action = request.getParameter("action");
		String editedImageName = null;
		
		if (action.equals("brightness") || action.equals("contrast")) {
			
			// Считываем переданные параметры.
			int brightness = Integer.parseInt(request.getParameter("value1"));
			int contrast = Integer.parseInt(request.getParameter("value2"));
			
			// Открываем изображение и применяем требуемые функции.
			BufferedImage inputImage = openImage((String) request.getSession().getAttribute("current_image"));
			BufferedImage resultImage = ImageFunctions.changeBrightness(inputImage, brightness);
			resultImage = ImageFunctions.changeContrast(resultImage, contrast);
			
			// Сохраняем отредактированное изображение под новым именем.
			editedImageName = GeneralFunctions.generateName();
			ImageIO.write(resultImage, "png", new File(GeneralFunctions.getPathToImages() + editedImageName + ".png"));
			
			// Удаляем возможную предыдущую "редакцию" исходного изображения.
			String oldEditedImageName = (String) request.getSession().getAttribute("temp_image");
			GeneralFunctions.deleteImage(oldEditedImageName);

			// Сохраняем имя нового отредактированного изображения в текущей сесии.
			request.getSession().setAttribute("temp_image", editedImageName);
			
		} else if (action.startsWith("rotate") || action.startsWith("reflect")) {
			
			// Проверяем, есть ли предыдущая "редакция" текущего изображения.
			String oldEditedImageName = (String) request.getSession().getAttribute("temp_image");
			
			// Открываем изображение (исходное либо предудущую "редакцию") и применяем требуемые функции.
			BufferedImage inputImage = null;
			if (oldEditedImageName == null) {
				inputImage = openImage((String) request.getSession().getAttribute("current_image"));
			} else {
				inputImage = openImage((String) request.getSession().getAttribute("temp_image"));
			}
			BufferedImage resultImage = null;
			if (action.equals("rotate_left")) resultImage = ImageFunctions.rotate(inputImage, 90);
			else if (action.equals("rotate_right")) resultImage = ImageFunctions.rotate(inputImage, -90);
			else if (action.equals("reflect_horizontal")) resultImage = ImageFunctions.reflect(inputImage, "horizontal");
			else if (action.equals("reflect_vertical")) resultImage = ImageFunctions.reflect(inputImage, "vertical");
			else resultImage = ImageFunctions.rotate(inputImage, 180);
			
			// Сохраняем отредактированное изображение под новым именем.
			editedImageName = GeneralFunctions.generateName();
			ImageIO.write(resultImage, "png", new File(GeneralFunctions.getPathToImages() + editedImageName + ".png"));
			
			// Удаляем возможную предыдущую "редакцию" исходного изображения.
			//String oldEditedImageName = (String) request.getSession().getAttribute("temp_image");
			GeneralFunctions.deleteImage(oldEditedImageName);

			// Сохраняем имя нового отредактированного изображения в текущей сесии.
			request.getSession().setAttribute("temp_image", editedImageName);
		}
		
		// Высылаем ответ javascript'у.
		response.getWriter().println(editedImageName);
	}

	/**
	 * Простая функция для загрузки изображения из папки "images" по указанному имени.
	 * 
	 * @param name - имя изображения без пути и расширения.
	 * @return Загруженное изображение типа BufferedImage.
	 * @throws IOException
	 */
	private static BufferedImage openImage(String name) throws IOException {
		InputStream file = new FileInputStream(GeneralFunctions.getPathToImages() + name + ".png");
		int fileSize = file.available();
		byte bytes[] = new byte[fileSize];
		file.read(bytes);
		file.close();
		return ImageIO.read(new ByteArrayInputStream(bytes));
	}
	
	/**
	 * Функция, выполняющая обрезку изображения в текущей сесии.
	 * 
	 * @param request
	 * @throws IOException 
	 */
	public static void applyCropping(HttpServletRequest request) throws IOException {
		// Открываем исходное изображение.
		BufferedImage inputImage = openImage((String) request.getSession().getAttribute("current_image"));
		float scaleFactor = (float) inputImage.getHeight() / 480;
		
		// Считываем переданные параметры.
		int x1 = Math.round(Integer.parseInt(request.getParameter("x1")) * scaleFactor);
		int y1 = Math.round(Integer.parseInt(request.getParameter("y1")) * scaleFactor);
		int x2 = Math.round(Integer.parseInt(request.getParameter("x2")) * scaleFactor);
		int y2 = Math.round(Integer.parseInt(request.getParameter("y2")) * scaleFactor);
		
		// Применяем кадрирование.
		BufferedImage resultImage = ImageFunctions.crop(inputImage, x1, y1, x2, y2);
		
		// Сохраняем отредактированное изображение под новым именем.
		String editedImageName = GeneralFunctions.generateName();
		ImageIO.write(resultImage, "png", new File(GeneralFunctions.getPathToImages() + editedImageName + ".png"));
		
		// Сохраняем имя нового отредактированного изображения в текущей сесии.
		request.getSession().setAttribute("current_image", editedImageName);
	}
	
	/**
	 * Функция, выполняющая преобразование изображения в серые тона в текущей сесии.
	 * 
	 * @param request
	 * @throws IOException
	 */
	public static void applyGrayscaleFilter(HttpServletRequest request) throws IOException {
		// Открываем исходное изображение и переводим в градации серого.
		BufferedImage inputImage = openImage((String) request.getSession().getAttribute("current_image"));
		BufferedImage resultImage = ImageFunctions.toGrayscale(inputImage);

		// Сохраняем отредактированное изображение под новым именем.
		String editedImageName = GeneralFunctions.generateName();
		ImageIO.write(resultImage, "png", new File(GeneralFunctions.getPathToImages() + editedImageName + ".png"));
		
		// Сохраняем имя нового отредактированного изображения в текущей сесии.
		request.getSession().setAttribute("current_image", editedImageName);
	}
	
	/**
	 * Функция, выполняющая инверсию цветов изображения в текущей сесии.
	 * 
	 * @param request
	 * @throws IOException
	 */
	public static void applyInversionFilter(HttpServletRequest request) throws IOException {
		// Открываем исходное изображение и инвертируем цвета.
		BufferedImage inputImage = openImage((String) request.getSession().getAttribute("current_image"));
		BufferedImage resultImage = ImageFunctions.invertColors(inputImage);

		// Сохраняем отредактированное изображение под новым именем.
		String editedImageName = GeneralFunctions.generateName();
		ImageIO.write(resultImage, "png", new File(GeneralFunctions.getPathToImages() + editedImageName + ".png"));
		
		// Сохраняем имя нового отредактированного изображения в текущей сесии.
		request.getSession().setAttribute("current_image", editedImageName);
	}
	
	/**
	 * Функция, вырезающая фрагмент изображения для предпросмотра
	 * при бесшовном копировании.
	 * 
	 * @param request
	 * @throws IOException
	 */
	public static void getCroppedFragment(HttpServletRequest request) throws IOException {
		// Открываем исходное изображение.
		BufferedImage inputImage = openImage((String) request.getSession().getAttribute("current_image"));
		float scaleFactor = (float) inputImage.getHeight() / 480;
		
		// Считываем переданные параметры.
		int x1 = Math.round(Integer.parseInt(request.getParameter("x1")) * scaleFactor);
		int y1 = Math.round(Integer.parseInt(request.getParameter("y1")) * scaleFactor);
		int x2 = Math.round(Integer.parseInt(request.getParameter("x2")) * scaleFactor);
		int y2 = Math.round(Integer.parseInt(request.getParameter("y2")) * scaleFactor);
		
		// Применяем кадрирование.
		BufferedImage resultImage = ImageFunctions.crop(inputImage, x1, y1, x2, y2);
		
		// Сохраняем отредактированное изображение под новым именем.
		String editedImageName = GeneralFunctions.generateName();
		ImageIO.write(resultImage, "png", new File(GeneralFunctions.getPathToImages() + editedImageName + ".png"));
		
		// Сохраняем имя нового отредактированного изображения в текущей сесии.
		request.getSession().setAttribute("fragment", editedImageName);
		
		// Сохраняем вспомогательные параметры.
		request.getSession().setAttribute("fragmentWidth", x2 - x1);
		request.getSession().setAttribute("fragmentHeight", y2 - y1);
		request.getSession().setAttribute("firstPointX", x1);
		request.getSession().setAttribute("firstPointY", y1);
	}
	
	/**
	 * Функция для расчета бесшовного копирования в текущей сесии.
	 * 
	 * @param request
	 * @throws IOException
	 */
	public static void applySeamlessCloning(HttpServletRequest request) throws IOException {
		// Удаляем фрагмент.
		GeneralFunctions.deleteImage((String) request.getSession().getAttribute("fragment"));
		
		// Открываем входные изображения.
		BufferedImage sourceImage = openImage((String) request.getSession().getAttribute("current_photo2"));
		BufferedImage destinationImage = openImage((String) request.getSession().getAttribute("current_photo1"));
		
		// Считываем необходимые параметры.
		int firstPointX = (Integer) request.getSession().getAttribute("firstPointX");
		int firstPointY = (Integer) request.getSession().getAttribute("firstPointY");
		int fragmentWidth = (Integer) request.getSession().getAttribute("fragmentWidth");
		int fragmentHeight = (Integer) request.getSession().getAttribute("fragmentHeight");
		int bindingPointX = Integer.parseInt(request.getParameter("bind_x"));
		int bindingPointY = Integer.parseInt(request.getParameter("bind_y"));
		
		// Применяем обработку.
		BufferedImage resultImage = ImageFunctions.seamlessCloning(sourceImage, destinationImage, firstPointX, firstPointY, fragmentWidth, fragmentHeight, bindingPointX, bindingPointY);
		
		// Сохраняем отредактированное изображение под новым именем.
		String editedImageName = GeneralFunctions.generateName();
		ImageIO.write(resultImage, "png", new File(GeneralFunctions.getPathToImages() + editedImageName + ".png"));
		
		// Сохраняем имя нового отредактированного изображения в текущей сесии.
		request.getSession().setAttribute("current_image", editedImageName);
		
		// Удаляем входные изображения.
		GeneralFunctions.deleteImage((String) request.getSession().getAttribute("current_photo2"));
		GeneralFunctions.deleteImage((String) request.getSession().getAttribute("current_photo1"));
		
		// Удаляем ненужные параметры сессии.
		request.getSession().removeAttribute("fragment");
		request.getSession().removeAttribute("fragmentWidth");
		request.getSession().removeAttribute("fragmentHeight");
		request.getSession().removeAttribute("firstPointX");
		request.getSession().removeAttribute("firstPointY");
	}

}
